﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using Microsoft.Win32;

namespace UniversalGrid
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        [DllImport("user32.dll")]
        public static extern int GetWindowLong(IntPtr hwnd, int index);

        [DllImport("user32.dll")]
        public static extern int SetWindowLong(IntPtr hwnd, int index, int newStyle);

        public const int WS_EX_TRANSPARENT = 0x00000020; public const int GWL_EXSTYLE = (-20);

        public int DefaultStyle;

        public MainWindow()
        {
            InitializeComponent();
            Loaded +=new RoutedEventHandler(MainWindow_Loaded);
            DataContext = App.ApplicationViewModel;
            App.ApplicationViewModel.GridList.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(GridList_CollectionChanged);
            App.ApplicationViewModel.PropertyChanged +=new System.ComponentModel.PropertyChangedEventHandler(ApplicationViewModel_PropertyChanged);
            RebuildGrid();
            App.ApplicationViewModel.AddGridCommand.Execute(null);
            ConfigWindow wnd = new ConfigWindow();
            wnd.Show();
        }

        void GridList_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.Action == NotifyCollectionChangedAction.Add)
            {
                foreach (GridViewModel newItem in e.NewItems)
                {
                    newItem.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(ApplicationViewModel_PropertyChanged);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove)
            {
                foreach (GridViewModel oldItem in e.OldItems)
                {
                    oldItem.PropertyChanged -= ApplicationViewModel_PropertyChanged;
                }
            }
            RebuildGrid();
        }

        void ApplicationViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "IsInClickThroughMode")
            {
                if (App.ApplicationViewModel.IsInClickThroughMode)
                {
                    // Get this window's handle         
                    IntPtr hwnd = new WindowInteropHelper(this).Handle;
                    // Change the extended window style to include WS_EX_TRANSPARENT         
                    DefaultStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
                    SetWindowLong(hwnd, GWL_EXSTYLE, DefaultStyle | WS_EX_TRANSPARENT);
                }
                else
                {
                    // Get this window's handle         
                    IntPtr hwnd = new WindowInteropHelper(this).Handle;
                    // Change the extended window style to include WS_EX_TRANSPARENT         
                    //            DefaultStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
                    SetWindowLong(hwnd, GWL_EXSTYLE, DefaultStyle);
                }
            }
            else if (e.PropertyName == "IsShowingBlindtext")
            {
                if (App.ApplicationViewModel.IsShowingBlindtext)
                {
                    blindText.Visibility = Visibility.Visible;
                }
                else
                {
                    blindText.Visibility = Visibility.Collapsed;
                }
            }
            else
            {
                RebuildGrid();    
            }

            
        }

        private void RebuildGrid()
        {
            if (App.ApplicationViewModel.IsBackgroundOpaque)
            {
                MainWnd.Background = new SolidColorBrush(Colors.White);
            }
            else
            {
                MainWnd.Background = null;
            }

            GridList.Children.Clear();
            foreach (var vm in App.ApplicationViewModel.GridList)
            {
                if (!CheckForDimensions(vm))
                {
                    continue;    
                }

                Grid OverlayGrid = new Grid();
                OverlayGrid.ColumnDefinitions.Clear();
                OverlayGrid.RowDefinitions.Clear();

                int colCount = 0;
                int rowCount = 0;
                GridLength colWidth = new GridLength();
                GridLength rowHeight = new GridLength();

                GridLength gutterWidth = new GridLength();
                GridLength gutterHeight = new GridLength();
                OverlayGrid.Width = Double.NaN;
                OverlayGrid.Height = Double.NaN;

                SetGridDimensionsAndAlignments(vm, OverlayGrid);

                switch (vm.ColumnCalculationType)
                {
                    case ColumnCalculationType.FixedWidth:
                        if (vm.ColumnWidth.HasValue)
                        {
                            colWidth = new GridLength(vm.ColumnWidth.Value, vm.ColumnType);
                            colCount = Convert.ToInt32(Math.Ceiling(App.ApplicationViewModel.ActualWidth / vm.ColumnWidth.Value));
                        }
                        else
                        {
                            colWidth = new GridLength(1, GridUnitType.Star);
                        }
                        break;
                    case ColumnCalculationType.FixedCount:
                        if (vm.ColumnCount.HasValue)
                        {
                            colWidth = new GridLength(1, GridUnitType.Star);
                            colCount = vm.ColumnCount.Value;
                        }
                        break;
                    case ColumnCalculationType.FixedBoth:
                        if (vm.ColumnWidth.HasValue)
                        {
                            colWidth = new GridLength(vm.ColumnWidth.Value, vm.ColumnType);
                        }
                        if (vm.ColumnCount.HasValue)
                        {
                            colCount = vm.ColumnCount.Value;
                        }
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                switch (vm.RowCalculationType)
                {
                    case RowCalculationType.FixedHeight:
                        if (vm.RowHeight.HasValue)
                        {
                            rowHeight = new GridLength(vm.RowHeight.Value, vm.RowType);
                            rowCount =
                                Convert.ToInt32(Math.Ceiling(App.ApplicationViewModel.ActualHeight/vm.RowHeight.Value));
                        }
                        else
                        {
                            rowHeight = new GridLength(1, GridUnitType.Star);
                        }
                        break;
                    case RowCalculationType.FixedCount:
                        if (vm.RowCount.HasValue)
                        {
                            rowHeight = new GridLength(1, GridUnitType.Star);
                            rowCount = vm.RowCount.Value;
                        }
                        break;
                    case RowCalculationType.Baseline:
                        if (vm.FontFamily != null && vm.FontSize.HasValue)
                        {
                            blindText.FontFamily = vm.FontFamily;

                            blindText.FontSize = vm.FontSize.Value;
                            blindText.FontStretch = vm.FontStretch;
                            blindText.FontStyle = vm.FontStyle;
                            blindText.FontWeight = vm.FontWeight;
                            var lineHeight = vm.FontSize.Value*vm.LineHeight * vm.FontFamily.LineSpacing;
                            blindText.LineHeight = vm.LineHeight* vm.FontFamily.LineSpacing *vm.FontSize.Value;
                            var baselineHeight = vm.FontSize.Value*vm.FontFamily.Baseline;
                            blindText.Padding = new Thickness(0, lineHeight - baselineHeight, 0, 0);

                            if (vm.IsFirstLineInCell)
                            {
                                rowHeight = new GridLength(lineHeight * (vm.CellHeight-1) + baselineHeight);
                                OverlayGrid.Margin = new Thickness(OverlayGrid.Margin.Left, OverlayGrid.Margin.Top + (lineHeight - baselineHeight),OverlayGrid.Margin.Right, OverlayGrid.Margin.Bottom);
                            }
                            else
                            {
                                rowHeight = new GridLength(lineHeight * vm.CellHeight);
                            }

                            rowCount = Convert.ToInt32(Math.Ceiling(App.ApplicationViewModel.ActualHeight / lineHeight));

                            //Baseline Grid

                            if (vm.IsBaselineGridVisible)
                            {
                                Grid baselineGrid = new Grid();
                                SetGridDimensionsAndAlignments(vm, baselineGrid);

                                for (int i = 0; i < rowCount; i++)
                                {
                                    var rowDef = new RowDefinition();
                                    rowDef.Height = new GridLength(lineHeight);
                                    baselineGrid.RowDefinitions.Add(rowDef);
                                }

                                ColorizeBaselineGrid(baselineGrid, vm);
                                GridList.Children.Add(baselineGrid);    
                            }
                            
                        }
                        break;
                    case RowCalculationType.FixedBoth:
                        if (vm.RowHeight.HasValue)
                        {
                            rowHeight = new GridLength(vm.RowHeight.Value, vm.RowType);
                        }
                        if (vm.RowCount.HasValue)
                        {
                            rowCount = vm.RowCount.Value;
                        }
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                switch (vm.GutterCalculationType)
                {
                    case GutterCalculationType.Fixed:
                        if (vm.GutterHeight.HasValue)
                        {
                            gutterHeight = new GridLength(vm.GutterHeight.Value, GridUnitType.Pixel);
                        }
                        else
                        {
                            gutterHeight = new GridLength(1, GridUnitType.Star);
                        }
                        
                        if (vm.GutterWidth.HasValue)
                        {
                            gutterWidth = new GridLength(vm.GutterWidth.Value, GridUnitType.Pixel);
                        }
                        else
                        {
                            gutterWidth = new GridLength(1, GridUnitType.Star);
                        }
                        break;
                    case GutterCalculationType.Baseline:
                        if (vm.GutterHeight.HasValue)
                        {
                            if (vm.IsFirstLineInCell)
                            {
                                var lineHeight = vm.FontSize.Value*vm.LineHeight * vm.FontFamily.LineSpacing;
                                var baselineHeight = vm.FontSize.Value*vm.FontFamily.Baseline;

                                gutterHeight =
                                    new GridLength(
                                        vm.GutterHeight.Value*vm.FontSize.Value*vm.LineHeight*vm.FontFamily.LineSpacing + (lineHeight - baselineHeight),
                                        GridUnitType.Pixel);    
                            }
                            else
                            {
                                gutterHeight = new GridLength(vm.GutterHeight.Value * vm.FontSize.Value * vm.LineHeight * vm.FontFamily.LineSpacing, GridUnitType.Pixel);
                            }
                            
                        }
                        else
                        {
                            if (vm.IsFirstLineInCell)
                            {
                                var lineHeight = vm.FontSize.Value*vm.LineHeight * vm.FontFamily.LineSpacing;
                                var baselineHeight = vm.FontSize.Value*vm.FontFamily.Baseline;

                                gutterHeight =
                                    new GridLength(
                                       (lineHeight - baselineHeight),
                                        GridUnitType.Pixel);
                            }
                            else
                            {
                                gutterHeight = new GridLength(1, GridUnitType.Star);    
                            }
                            
                        }
                        
                        if (vm.GutterWidth.HasValue)
                        {
                            gutterWidth = new GridLength(vm.GutterWidth.Value * vm.FontSize.Value * vm.LineHeight * vm.FontFamily.LineSpacing, GridUnitType.Pixel);
                        }
                        else
                        {
                            gutterWidth = new GridLength(1, GridUnitType.Star);
                        }
                        break;
                    case GutterCalculationType.Ratio:
                        if (vm.GutterHeight.HasValue)
                        {
                            if (rowHeight.IsAbsolute)
                            {
                                gutterHeight = new GridLength(((rowHeight.Value/100)*vm.GutterHeight.Value),
                                                              GridUnitType.Pixel);
                            }
                            else
                            {
                                gutterHeight = new GridLength(((rowHeight.Value / 100) * vm.GutterHeight.Value),
                                                              GridUnitType.Star);
                            }
                            
                        }
                        else
                        {
                            gutterHeight = new GridLength(1, GridUnitType.Star);
                        }
                        
                        if (vm.GutterWidth.HasValue)
                        {
                            if (colWidth.IsAbsolute)
                            {
                                gutterWidth = new GridLength((colWidth.Value / 100) * vm.GutterWidth.Value, GridUnitType.Pixel);
                            }
                            else
                            {
                                gutterWidth = new GridLength((colWidth.Value / 100) * vm.GutterWidth.Value, GridUnitType.Star);    
                            }
                            
                        }
                        else
                        {
                            gutterWidth = new GridLength(1, GridUnitType.Star);
                        }
                        break;
                    default:
                        throw new ArgumentOutOfRangeException();
                }

                if (vm.IsStartingWithGutter)
                {
                    var firstGutterCol = new ColumnDefinition();
                    firstGutterCol.Width = gutterWidth;
                    OverlayGrid.ColumnDefinitions.Add(firstGutterCol);

                    var firstGutterRow = new RowDefinition();
                    firstGutterRow.Height = gutterHeight;
                    OverlayGrid.RowDefinitions.Add(firstGutterRow);
                }

                for (int i = 0; i < colCount; i++)
                {
                    var colDef = new ColumnDefinition();
                    colDef.Width = colWidth;
                    OverlayGrid.ColumnDefinitions.Add(colDef);
                    if (vm.GutterWidth.HasValue)
                    {
                        var gutterDef = new ColumnDefinition();
                        gutterDef.Width = gutterWidth;
                        OverlayGrid.ColumnDefinitions.Add(gutterDef);   
                    }
                }

                for (int i = 0; i < rowCount; i++)
                {
                    var rowDef = new RowDefinition();
                    rowDef.Height = rowHeight;
                    OverlayGrid.RowDefinitions.Add(rowDef);
                    if (vm.GutterHeight.HasValue || vm.IsFirstLineInCell)
                    {
                        var gutterDef = new RowDefinition();
                        gutterDef.Height = gutterHeight;
                        OverlayGrid.RowDefinitions.Add(gutterDef);
                    }
                }   
                ColorizeGrid(vm,OverlayGrid);
                GridList.Children.Add(OverlayGrid);
            }
        }

        private bool CheckForDimensions(GridViewModel vm)
        {
            if (App.ApplicationViewModel.ActualHeight < vm.MinHeight ||
                App.ApplicationViewModel.ActualHeight > vm.MaxHeight)
            {
                return false;
            }
            if (App.ApplicationViewModel.ActualWidth < vm.MinWidth ||
            App.ApplicationViewModel.ActualWidth > vm.MaxWidth)
            {
                return false;
            }
            return true;
        }

        private static void SetGridDimensionsAndAlignments(GridViewModel vm, Grid OverlayGrid)
        {
            if (vm.Width.HasValue)
            {
                OverlayGrid.Width = vm.Width.Value;
            }
            else
            {
                OverlayGrid.Width = App.ApplicationViewModel.ActualWidth;
            }

            if (vm.Height.HasValue)
            {
                OverlayGrid.Height = vm.Height.Value;
            }
            else
            {
                OverlayGrid.Height = App.ApplicationViewModel.ActualHeight;
            }

            OverlayGrid.HorizontalAlignment = vm.HorizontalAlignment;
            OverlayGrid.VerticalAlignment = vm.VerticalAlignment;

            Thickness margin = new Thickness();

            if (vm.LeftMargin.HasValue)
            {
                margin.Left = vm.LeftMargin.Value;
            }
            if (vm.TopMargin.HasValue)
            {
                margin.Top = vm.TopMargin.Value;
            }
            if (vm.RightMargin.HasValue)
            {
                margin.Right = vm.RightMargin.Value;
            }
            if (vm.BottomMargin.HasValue)
            {
                margin.Bottom = vm.BottomMargin.Value;
            }
            OverlayGrid.Margin = margin;
        }

        private void ColorizeGrid(GridViewModel vm,Grid OverlayGrid)
        {
            int cols = OverlayGrid.ColumnDefinitions.Count;
            int rows = OverlayGrid.RowDefinitions.Count;

            OverlayGrid.Children.Clear();

            for (int i = 0; i < cols; i++)
            {
                for (int j = 0; j < rows; j++)
                {
                    Border border = new Border();
                    Grid.SetColumn(border,i);
                    Grid.SetRow(border,j);
                    border.BorderBrush = new SolidColorBrush(vm.GridColor);
                    var thick = new Thickness();
                    thick.Right = vm.GridThickness;
                    thick.Bottom = vm.GridThickness;

                    if (j == 0)
                    {
                        thick.Top = vm.GridThickness;
                    }
                    if (i == 0)
                    {
                        thick.Left = vm.GridThickness;
                    }
                    border.BorderThickness = thick;
                    border.Opacity = vm.GridOpacity;

                    int k = 1;
                    
                    if (vm.IsStartingWithGutter)
                    {
                        k = 0;
                    }
 
                    if (i%2 == k && vm.GutterWidth.HasValue)
                    {
                        Border gutterBorder = new Border();
                        gutterBorder.Background = new SolidColorBrush(vm.GutterBackground);
                        gutterBorder.Opacity = vm.GutterOpacity;
                        border.Child = gutterBorder;
                    }
                    if (j % 2 == k && vm.GutterHeight.HasValue)
                    {
                        Border gutterBorder = new Border();
                        gutterBorder.Background = new SolidColorBrush(vm.GutterBackground);
                        gutterBorder.Opacity = vm.GutterOpacity;
                        border.Child = gutterBorder;
                    }
                    OverlayGrid.Children.Add(border);
                }
            }
            OverlayGrid.UpdateLayout();
        }

        private void ColorizeBaselineGrid(Grid baselineGrid, GridViewModel vm)
        {
            int rows = baselineGrid.RowDefinitions.Count;

            baselineGrid.Children.Clear();

            for (int j = 0; j < rows; j++)
            {
                Border border = new Border();
                Grid.SetRow(border, j);
                border.BorderBrush = new SolidColorBrush(vm.BaselineGridColor);
                border.BorderThickness = new Thickness(0, 0, vm.BaselineGridThickness, vm.BaselineGridThickness);
                border.Opacity = vm.BaselineGridOpacity;
                baselineGrid.Children.Add(border);
            }
            baselineGrid.UpdateLayout();
        }


        void MainWindow_Loaded(object sender, RoutedEventArgs e)
        {
            // Get this window's handle         
            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            // Change the extended window style to include WS_EX_TRANSPARENT         
//            DefaultStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
//            SetWindowLong(hwnd, GWL_EXSTYLE, DefaultStyle | WS_EX_TRANSPARENT);  

        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            ConfigWindow wnd = new ConfigWindow();
            wnd.Show();
            // Get this window's handle         
//            IntPtr hwnd = new WindowInteropHelper(this).Handle;
            // Change the extended window style to include WS_EX_TRANSPARENT         
//            DefaultStyle = GetWindowLong(hwnd, GWL_EXSTYLE);
//            SetWindowLong(hwnd, GWL_EXSTYLE, DefaultStyle);
        }

        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        private void MainWindow_OnSizeChanged(object sender, SizeChangedEventArgs e)
        {
            Console.WriteLine(e.NewSize);
        }

        private void UIElement_OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            DragMove();
        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            
        }
    }
}
